package graphics.shapes;

import graphics.tools.BallPosition;
import graphics.tools.ShapeData;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.media.j3d.Appearance;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Material;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.swing.JOptionPane;
import javax.swing.Timer;

import com.sun.j3d.utils.geometry.Cylinder;

/**
 * A BranchGroup depicting a GeoMag Rod.
 * 
 * @author: Eirik Vigeland
 * @version: %I%, %G%
 */
public class Rod extends BranchGroup implements ActionListener {

	private Appearance ap;
	private Material ma;
	private Cylinder shape;
	private TransformGroup tg;
	private boolean state;
	private TransparencyAttributes ta;
	private Ball ball, parentNode;
	private int PositionOfRod, rotation;
	private BranchGroup bg;
	private Timer timer;
	private double angle = 0.0d;
	private Bounds shapeBounds;

	/**
	 * Constructor setting the functions and settings of the GeoMag sphere.
	 * 
	 * @param rotation
	 *            Manages the axis of rotation
	 */
	public Rod(int rotation, Ball parentNode) {
		this.parentNode = parentNode;
		this.rotation = rotation;
		ap = new Appearance();
		ma = new Material();
		ta = new TransparencyAttributes();
		ap.setTransparencyAttributes(ta);
		ap.setMaterial(ma);
		timer = new Timer(5000, this);

		shape = new Cylinder((float) ShapeData.RODRADIUS,
				(float) ShapeData.RODLENGTH, ap);
		setTransparent(true);
		tg = new TransformGroup();
		tg.addChild(shape);
		capability();

		addChild(tg);
	}

	/**
	 * Setting the capability allowing for setting the transparency and
	 * addition/removal of GeoMag balls
	 */
	private void capability() {
		ma.setCapability(Material.ALLOW_COMPONENT_READ);
		ma.setCapability(Material.ALLOW_COMPONENT_WRITE);
		ta.setCapability(TransparencyAttributes.ALLOW_MODE_READ);
		ta.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE);
		ta.setCapability(TransparencyAttributes.ALLOW_VALUE_READ);
		ta.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
		tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		tg.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		setCapability(BranchGroup.ALLOW_DETACH);
		setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
		setCapability(BranchGroup.ALLOW_CHILDREN_READ);
		setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
	}

	/**
	 * Remove the rod from a sphere, if the rod is the parent of the sphere
	 */
	private void removeThisRod() {
		if (getPositionOfRod() == ShapeData.TOP) {
			ball.exclude(ShapeData.BOTTOM);
		} else if (getPositionOfRod() == ShapeData.BOTTOM) {
			ball.exclude(ShapeData.TOP);
		} else if (getPositionOfRod() == ShapeData.RIGHT) {
			ball.exclude(ShapeData.LEFT);
		} else if (getPositionOfRod() == ShapeData.LEFT) {
			ball.exclude(ShapeData.RIGHT);
		} else if (getPositionOfRod() == ShapeData.FRONT) {
			ball.exclude(ShapeData.BACK);
		} else if (getPositionOfRod() == ShapeData.BACK) {
			ball.exclude(ShapeData.FRONT);
		}
	}

	/**
	 * Show the options of the rod when click
	 * 
	 * @param bg
	 *            The SceneGraph to where the GeoMag Sphere is added.
	 */
	public void addBall(BranchGroup bg) {
		this.bg = bg;
		timer.start();
		ball = new Ball(parentNode.getCd());
		ball.setParentRod(this);
		removeThisRod();
		ball.setTransparent(true);
		Transform3D tr = ball.getTransform3D();
		tr.setTranslation(new BallPosition(this));
		ball.getTransformGroup().setTransform(tr);
		ball.setOrigo();
		ball.colorColliding();
		bg.addChild(ball);
	}

	/**
	 * Sets the transparency of the rod for selection
	 * 
	 * @param state
	 *            Describes the state of the balls transparency
	 */
	@SuppressWarnings("static-access")
	public void setTransparent(boolean state) {
		this.state = state;
		if (state) {
			ta.setTransparencyMode(ta.FASTEST);
			ta.setTransparency(0.5f);
		} else {
			ta.setTransparencyMode(ta.NONE);
		}
	}

	/**
	 * Returns the current Transformation of the GeoMag Rod
	 * 
	 * @return Transform3D with the current Transformations.
	 */
	public Transform3D getTransform3D() {
		Transform3D tr = new Transform3D();
		tg.getTransform(tr);
		return tr;
	}

	/**
	 * Hides the ball if not selected
	 */
	public void hideBall() {
		if (ball.isTransparent()) {
			bg.removeChild(ball);
		}
	}

	/**
	 * Shows the ball
	 */
	public void showBall() {
		if (bg.indexOfChild(ball) == -1) {
			ball.getCd().add(ball.getShapeBounds());
			ball.colorColliding();
			bg.addChild(ball);
		}
	}

	/**
	 * Attach the ball to the SceneGraph for construction
	 */
	public void attachBall() {
		if (ball.getCd().isColliding(ball.getShapeBounds())) {
			JOptionPane.showMessageDialog(null,
					"This item is in collision with another item!", "Collision!",
					JOptionPane.WARNING_MESSAGE);
		} else {
			ball.setTransparent(false);
			ball.hasParent();
		}

	}

	/**
	 * Removes the ball created by this rod from the GeoMag Sphere
	 * 
	 * @param ball
	 *            The ball to be removed
	 */
	public void removeBall(Ball ball) {
		ball.setTransparent(true);
		bg.removeChild(ball);
		ball.getCd().remove(ball.getShapeBounds());
	}
	
	/**
	 * Sets a red color on collision
	 */
	public void colorColliding() {
		if (parentNode.getCd().isColliding(getShapeBounds()))
			ma.setEmissiveColor(1.0f, 0, 0);
		else
			ma.setEmissiveColor(0, 0, 0);
	}

	/**
	 * Returns the bounds used for collision detection
	 * @return The BoundingSphere for collision detection
	 */
	public Bounds getShapeBounds() {
		return shapeBounds;
	}

	/**
	 * Sets the bounds for collision detection
	 * @param shapeBounds The bounds to be set
	 */
	public void setShapeBounds(Bounds shapeBounds) {
		this.shapeBounds = shapeBounds;
	}

	/**
	 * The TransformGroup of the rod
	 * 
	 * @return TransformGroup in use by the sphere
	 */
	public TransformGroup getTransformGroup() {
		return tg;
	}

	/**
	 * Sets the angel at which the rod is placed
	 * 
	 * @param angle
	 *            Angel in radiant
	 */
	public void setAngle(double angle) {
		this.angle = angle;
	}

	/**
	 * Returns the current angel of the rod
	 * 
	 * @return Angel in radiant
	 */
	public double getAngle() {
		return angle;
	}

	/**
	 * The position of the rod on the sphere (whether its the top, bottom,
	 * front, back, left or right);
	 * 
	 * @param positionOfRod
	 *            An INT depicting the current position
	 */
	public void setPositionOfRod(int positionOfRod) {
		this.PositionOfRod = positionOfRod;
	}

	/**
	 * The state of the axis of rotation
	 * 
	 * @return The axis of rotation
	 */
	public int getRotation() {
		return rotation;
	}

	/**
	 * The current position of the rod on the sphere.
	 * 
	 * @return An INT depicting the current position
	 */
	public int getPositionOfRod() {
		return PositionOfRod;
	}

	/**
	 * Whether or not the rod is transparent
	 * 
	 * @return The state of transparency
	 */
	public boolean isTransparent() {
		return state;
	}

	/**
	 * Returns the ball connected to this rod
	 * 
	 * @return Ball
	 */
	public Ball getBall() {
		return ball;
	}

	/**
	 * Returns the parent ball connected to this rod
	 * 
	 * @return Ball
	 */
	public Ball getParentNode() {
		return parentNode;
	}

	/**
	 * Hides the connected ball when the timer runs out (if not selected)
	 * 
	 * @param e
	 *            Activated when the timer runs out.
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == timer) {
			hideBall();
		}

	}

}
